using System;
using System.Collections;
using System.Collections.Specialized;
using System.Windows.Threading;

namespace TrueCode.WPF
{
  public class ThreadSafeCollection : IList, INotifyCollectionChanged
  {
    #region Fields
    private readonly Dispatcher _dispatcher;
    private readonly IList _source;
    #endregion

    #region .ctors
    public ThreadSafeCollection( Dispatcher dispatcher, object source )
    {
      #region Validaton
      if( dispatcher == null ) throw new ArgumentNullException( "dispatcher" );
      if( source == null ) throw new ArgumentNullException( "source" );

      if( !( source is IList ) || !( source is INotifyCollectionChanged ) )
        throw new ArgumentException( "Collection must implement IList and INotifyCollectionChanged interfaces", "source" );
      #endregion

      _dispatcher = dispatcher;
      _source = ( IList )source;

      var eventSource = ( INotifyCollectionChanged )source;
      eventSource.CollectionChanged += OnSourceCollectionChanged;
    }
    #endregion

    #region Entry points
    private void OnSourceCollectionChanged( object sender, NotifyCollectionChangedEventArgs e )
    {
      if( _dispatcher.CheckAccess() )
        RaiseCollectionChanged( e );
      else
        _dispatcher.BeginInvoke( ( Action< NotifyCollectionChangedEventArgs > )RaiseCollectionChanged, DispatcherPriority.Send, e );
    }
    #endregion

    #region Implementation of IList
    public IEnumerator GetEnumerator()
    {
      return _source.GetEnumerator();
    }

    public void CopyTo( Array array, int index )
    {
      _source.CopyTo( array, index );
    }

    public int Count
    {
      get { return _source.Count; }
    }

    public object SyncRoot
    {
      get { return _source.SyncRoot; }
    }

    public bool IsSynchronized
    {
      get { return _source.IsSynchronized; }
    }

    public int Add( object value )
    {
      return _source.Add( value );
    }

    public bool Contains( object value )
    {
      return _source.Contains( value );
    }

    public void Clear()
    {
      _source.Clear();
    }

    public int IndexOf( object value )
    {
      return _source.IndexOf( value );
    }

    public void Insert( int index, object value )
    {
      _source.Insert( index, value );
    }

    public void Remove( object value )
    {
      _source.Remove( value );
    }

    public void RemoveAt( int index )
    {
      _source.RemoveAt( index );
    }

    public object this[ int index ]
    {
      get { return _source[ index ]; }
      set { _source[ index ] = value; }
    }

    public bool IsReadOnly
    {
      get { return _source.IsReadOnly; }
    }

    public bool IsFixedSize
    {
      get { return _source.IsFixedSize; }
    }
    #endregion

    #region Implementation of INotifyCollectionChanged
    public event NotifyCollectionChangedEventHandler CollectionChanged;

    private void RaiseCollectionChanged( NotifyCollectionChangedEventArgs e )
    {
      var @event = CollectionChanged;
      if( @event != null )
        @event( this, e );
    }
    #endregion
  }
}